/*

 * Copyright (c) 2017-2018, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package of.hotswapinject.agent;

import of.hotswapinject.core.attach.IOnAttach;
import of.hotswapinject.core.common.Util;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import of.hotswapinject.core.plugin.IMethodFilter;
import of.hotswapinject.core.plugin.PluginInitalize;
import of.hotswapinject.core.search.ISearch;

import java.lang.instrument.Instrumentation;
import java.util.*;

public class PluginInstallContext {
    public final Options options = new Options();
    public final Map<String, IMethodFilter> methodFilterMap = new HashMap<String, IMethodFilter>();
    public final Map<String, ISearch> searchMap = new HashMap<String, ISearch>();
    public final List<IOnAttach> onAttachList = new ArrayList<IOnAttach>();

    public List<IMethodFilter> getMethodInjectors(CommandLine cmd) {
        List<IMethodFilter> result = new ArrayList<IMethodFilter>();
        for(Map.Entry<String, IMethodFilter> entry : methodFilterMap.entrySet()) {
            if(cmd.hasOption(entry.getKey())) {
                result.add(entry.getValue());
            }
        }
        return result;
    }

    public List<String> init(String jarPath) throws Exception {
        PluginInitalize.PluginManager pluginManager = PluginInitalize.getInstance().reloadPlugins(jarPath);

        Option listLoaeded = new Option("fl", "filterLoaded", false, "filter loaded classes");
        listLoaeded.setRequired(false);
        options.addOption(listLoaeded);

        Option list = new Option("l", "list", false, "list all classes");
        list.setRequired(false);
        options.addOption(list);

        Option output = new Option("o", "output", true, "output finded class into file");
        output.setRequired(false);
        options.addOption(output);

        Option dump = new Option("d", "dump", true, "dump class transformed to path");
        dump.setRequired(false);
        options.addOption(dump);

        Option pid = new Option("p", "pid", true, "attach to java process id(use jps to view)");
        pid.setRequired(false);
        options.addOption(pid);

        Option jps = new Option("jps", "javaProcess", false, "list java process.");
        jps.setRequired(false);
        options.addOption(jps);

        for(IMethodFilter mf : pluginManager.methodFilterList) {
            Option op = mf.getOption();
            options.addOption(op);
            methodFilterMap.put(op.getLongOpt(), mf);
        }

        for(ISearch sc : pluginManager.searchList) {
            Option op = sc.getOption();
            options.addOption(op);
            searchMap.put(op.getLongOpt(), sc);
        }

        for(IOnAttach oa : pluginManager.onAttachList) {
            onAttachList.add(oa);
        }

        HashSet<String> optSet = new HashSet<String>();
        for(Option op : options.getOptions()) {
            String opt = op.getOpt();
            if(optSet.contains(opt)) {
                throw new RuntimeException("Opt " + opt + " duplicate defined!");
            }
            optSet.add(opt);
        }

        optSet = new HashSet<String>();
        for(Option op : options.getOptions()) {
            String opt = op.getLongOpt();
            if(optSet.contains(opt)) {
                throw new RuntimeException("LongOpt " + opt + " duplicate defined!");
            }
            optSet.add(opt);
        }
        return pluginManager.pluginPathList;
    }

    public void onAttach(String args, Instrumentation inst) {
        for(IOnAttach oa : onAttachList) {
            oa.onAttach(args, inst);
        }
    }
}
